//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdio.h>
#include <commuTypes.h>
#include <commuObj.h>
#include <commuErr.h>
#include <windows.h>

#define PIPE_RECV_NAME L"\\\\.\\pipe\\multi_recv"
#define PIPE_SEND_NAME L"\\\\.\\pipe\\multi_send"
#define BUFSIZE   4096

HANDLE g_recv_handle;
HANDLE g_send_handle;
HANDLE g_recv_mutex;
HANDLE g_send_mutex;

char *receive_buffer;
unsigned long read_pos = 0;
unsigned long receive_len = 0;

EXTERN_C UINT __stdcall read_once();

EXTERN_C void DisconncetPipe()
{
    FlushFileBuffers(g_send_handle);
    DisconnectNamedPipe(g_send_handle);
    CloseHandle(g_send_handle);
}

EXTERN_C int ChannelInitPort()
{
    BOOL fConnected = FALSE;

    g_recv_handle = CreateNamedPipeW(
            PIPE_RECV_NAME,                // pipe name
            PIPE_ACCESS_DUPLEX,       // read/write access
            PIPE_TYPE_MESSAGE |       // message type pipe
            PIPE_READMODE_MESSAGE |   // message-read mode
            PIPE_WAIT,                // blocking mode
            PIPE_UNLIMITED_INSTANCES, // max. instances
            BUFSIZE,                  // output buffer size
            BUFSIZE,                  // input buffer size
            NMPWAIT_USE_DEFAULT_WAIT, // client time-out
            NULL);                    // default security attribute

    if (g_recv_handle == INVALID_HANDLE_VALUE) {
        printf("CreatePipe failed");
        return -1;
    }

    g_send_handle = CreateNamedPipeW(
            PIPE_SEND_NAME,                // pipe name
            PIPE_ACCESS_DUPLEX,       // read/write access
            PIPE_TYPE_MESSAGE |       // message type pipe
            PIPE_READMODE_MESSAGE |   // message-read mode
            PIPE_WAIT,                // blocking mode
            PIPE_UNLIMITED_INSTANCES, // max. instances
            BUFSIZE,                  // output buffer size
            BUFSIZE,                  // input buffer size
            NMPWAIT_USE_DEFAULT_WAIT, // client time-out
            NULL);                    // default security attribute

    if (g_send_handle == INVALID_HANDLE_VALUE) {
        printf("CreatePipe failed");
        return -1;
    }

    fConnected = ConnectNamedPipe(g_recv_handle, NULL) ?
        TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

    if (!fConnected) {
        printf("IpcServerWaitForConnect failed\n");
        return -1;
    }

    fConnected = ConnectNamedPipe(g_send_handle, NULL) ?
        TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

    if (!fConnected) {
        printf("IpcServerWaitForConnect failed\n");
        return -1;
    }

    // Create a mutex with no initial owner.

    g_recv_mutex = CreateMutexW(
        NULL,                       // default security attributes
        FALSE,                      // initially not owned
        NULL);                      // unnamed mutex

    if (g_recv_mutex == NULL)
    {
        printf("CreateMutex error: %d\n", GetLastError());
    }

    g_send_mutex = CreateMutexW(
        NULL,                       // default security attributes
        FALSE,                      // initially not owned
        NULL);                      // unnamed mutex

    if (g_send_mutex == NULL)
    {
        printf("CreateMutex error: %d\n", GetLastError());
    }

    receive_buffer = (char *)malloc(BUFSIZE * sizeof(char));
    if (NULL == receive_buffer) {
        printf("alloc initial buffer failed\n");
    }

    return 0;
}

EXTERN_C int ChannelSendData(void *data, int size, int *sendSize) {
    BOOL fSuccess;
    int send_count;

    WaitForSingleObject(g_send_mutex, INFINITE);

    fSuccess = WriteFile(
            g_send_handle, // handle to pipe
            data,             // buffer to write from
            size,         // number of bytes to write
            (DWORD *)&send_count,       // number of bytes written
            NULL);            // not overlapped I/O

    if (!ReleaseMutex(g_send_mutex)) {
        return -1;
    }

    if (!fSuccess || size != send_count) {
        DisconncetPipe();
        return -1;
    }

    if (NULL != sendSize) {
        *sendSize = send_count;
    }

    return 0;
}

EXTERN_C int ChannelReceiveData(void *data, int size, int *receiveSize) {
    int retValue = 0;
    int index = 0;

    WaitForSingleObject(
    g_recv_mutex,   // handle to mutex
    INFINITE);   // five-second time-out interval

    while (index < size) {
        if (read_pos < receive_len) {
            ((char *)data)[index++] = receive_buffer[read_pos++];
        }
        else {
            retValue = read_once();
            if (0 != retValue) {
                return C_CLOSED;
            }
        }
    }

    if (!ReleaseMutex(g_recv_mutex)) {
        return -1;
    }

    if (NULL != receiveSize) {
        *receiveSize = size;
    }

    return 0;
}

EXTERN_C UINT __stdcall read_once()
{
    BOOL fSuccess;

    do {
        read_pos = 0;
        // Read the reply from the server.
        fSuccess = ReadFile(
                g_recv_handle,       // pipe handle
                receive_buffer,          // buffer to receive reply
                BUFSIZE,                 // size of buffer
                &receive_len,            // number of bytes read
                NULL);                   // not overlapped

        if (!fSuccess) {
            printf("<IpcInvoke > (ReadFile)Error = %d\n",GetLastError());
            return 0xffffffff;
        }
    } while (0);        // repeat loop if ERROR_MORE_DATA

    return 0;
}
